#include "head.h"
#include <func.h>
int exitPipe[2];
void sigFunc(int signum){
    puts("signal is coming!");
    write(exitPipe[1], "1", 1);
}

int main(int argc, char *argv[]){
    // ./server 192.168.31.128 1234 3
    ARGS_CHECK(argc, 4);
    openlog(NULL,LOG_PID,LOG_USER);
    pipe(exitPipe);
    if(fork() != 0){
        close(exitPipe[0]);//close read pipe
        signal(SIGQUIT, sigFunc);
        wait(NULL);
        //清空UserLoginInformation表的token列
        MYSQL *db = mysql_init(NULL);
        initDataBase(db);
        setTokenNull(db);
        mysql_close(db);
        exit(0);
    }
    close(exitPipe[1]);//close write pipe
    int workerNum = atoi(argv[3]);
    threadPool_t threadPool;
    threadPoolInit(&threadPool, workerNum);
    int sockFd;
    makeWorker(&threadPool);
    tcpInit(argv[1], argv[2], &sockFd);
    int epFd = epoll_create(10);
    epollAdd(sockFd, epFd);
    epollAdd(exitPipe[0], epFd);
    struct epoll_event readyList[2];

    MYSQL *db = mysql_init(NULL);
    initDataBase(db);

    //每次打开服务器都生成新的盐值
    char salt[50] = {0};
    getSalt(salt);

    while(1){
        int readyNum = epoll_wait(epFd, readyList, 2, -1);
        puts("epoll ready");
        for(int i=0; i<readyNum; ++i){
            if(readyList[i].data.fd == sockFd){
                int netFd = accept(sockFd, NULL, NULL);
                // printf("netfd = %d\n", netFd);
                
                char user_Name[100] = {0};
                int length;
                recv(netFd, &length, sizeof(length), 0);
                // printf("%d\n", dataLength);
                recv(netFd, user_Name, length, 0);

                char  type;
                recv(netFd, &type, 1, 0);

                if(type == '0'){//新用户注册登录
                    char choice;
                    recv(netFd, &choice, 1, 0);
                    
                    int dataLength;
                    recv(netFd, &dataLength, sizeof(dataLength), 0);
                    // printf("%d\n", dataLength);
                    char buf[1024] = {0};
                    recv(netFd, buf, dataLength, 0);
                    // printf("%s\n", buf);
                    char arr[100] = "";
                    strcpy(arr, buf);
                    char *userName = strtok(arr, " ");
                    char *password = strtok(NULL, " ");
                    //strcpy(user_Name, userName);
                    
                    char token[100] = {0};
                    makeToken(salt, userName, token);//生成token
                    if(choice == '1'){//登录
                        int isLoginUsrLegal = usrLogin(db, userName, password);
                        send(netFd, &isLoginUsrLegal, 1, MSG_NOSIGNAL);
                        if(isLoginUsrLegal == 1 || isLoginUsrLegal == 2){//密码错误
                            close(netFd);
                            continue;
                        }
                        //如果用户登录信息正确，token存入数据库，并且给客户端发送token值
    
                        //该用户为合法用户，但尚未在本地登录，需要向数据库存入其token值
                        // char file_name[255] = {0};
                        // sprintf(file_name, "/%s", userName);
                        insertTokenToUserInformation(db, userName, token);
                    }
                    else if(choice == '2'){//注册
                        int isRegisterRight = usrRegisterWithToken(db, userName, password, token);
                        if(isRegisterRight == 0){//注册需要生成token并插表
                            send(netFd, "0", 1, MSG_NOSIGNAL);
                            insertCatalogWhenRegister(db, userName);
                        }
                        else{
                            send(netFd, "1", 1, MSG_NOSIGNAL);
                        }
                    }
                    train_t token_train;
                    bzero(&token_train, sizeof(token_train));
                    token_train.dataLength = strlen(token);
                    strcpy(token_train.buf, token);
                    int ret = send(netFd, &token_train, sizeof(token_train.dataLength)+token_train.dataLength, MSG_NOSIGNAL);
                    ERROR_CHECK(ret, -1, "send token");//向客户端发送当前token                    
                }
                else if(type == '1'){//旧用户登录
                    //通过用户名和发送来的token与数据库匹配
                    //三种可能
                    //1.数据库token==NULL，旧用户信息过期，需要判断用户是否合法
                    //2.token不匹配，为非法用户
                    //3.token匹配--->合法用户
                    int length;
                    char usrToken[100] = {0};
                    recv(netFd, &length, sizeof(int), 0);
                    recv(netFd, usrToken, length, 0);
                    char usr_name[50] = {0};
                    bzero(&length, sizeof(length));
                    recv(netFd, &length, sizeof(int), 0);
                    recv(netFd, usr_name, length, 0);
                    //通过用户名得到真正的token
                    char token[100] = {0};
                    int ret = findTokenWithUsrName(db, usr_name, token);
                    if(ret == 1){//token为空，用户token过期，重新验证用户信息
                        send(netFd, "1", 1, MSG_NOSIGNAL);
                        //重新验证用户信息
                        int dataLength;
                        recv(netFd, &dataLength, sizeof(dataLength), 0);
                        // printf("%d\n", dataLength);
                        char buf[1024] = {0};
                        recv(netFd, buf, dataLength, 0);
                        // printf("%s\n", buf);
                        char arr[100] = "";
                        strcpy(arr, buf);
                        char *userName = strtok(arr, " ");
                        char *password = strtok(NULL, " ");

                        int isLoginUsrLegal = usrLogin(db, userName, password);
                        send(netFd, &isLoginUsrLegal, 1, MSG_NOSIGNAL);
                        if(isLoginUsrLegal == 1 || isLoginUsrLegal == 2){//密码错误
                            close(netFd);
                            continue;
                        }
                        //如果用户登录信息正确，token存入数据库，并且给客户端发送token值
                        //该用户为合法用户，需要向数据库存入其token值，并在其文件夹写入新的token
                        insertTokenToUserInformation(db, userName, token);
                        train_t token_train;
                        bzero(&token_train, sizeof(token_train));
                        token_train.dataLength = strlen(token);
                        strcpy(token_train.buf, token);
                        int ret = send(netFd, &token_train, sizeof(token_train.dataLength)+token_train.dataLength, MSG_NOSIGNAL);
                        ERROR_CHECK(ret, -1, "send token");//向客户端发送当前token
                    }
                    else if(ret == 0){//token不为空，有匹配和不匹配两种情况
                        send(netFd, "0", 1, MSG_NOSIGNAL);
                        if(strcmp(usrToken, token) == 0){
                            send(netFd, "0", 1, MSG_NOSIGNAL);
                        }
                        else{//不匹配，非法用户
                            send(netFd, "1", 1, MSG_NOSIGNAL);
                        }
                    }
                    else{
                        send(netFd, "2", 1, MSG_NOSIGNAL);
                        puts("Wrong return");
                        continue;
                    }
                }





                

                // //记录用户上线
                // time_t ttime;
                // time(&ttime);
                // struct tm *pTm = gmtime(&ttime);
                // syslog(LOG_INFO, "%s %04d:%02d:%02d", userName, (1900 + pTm->tm_year), (1 + pTm->tm_mon), (pTm->tm_mday));

                pthread_mutex_lock(&threadPool.mutex);
                enUserQueue(&threadPool.userQueue, netFd, user_Name);
                puts("New Task");
                pthread_cond_signal(&threadPool.cond);
                pthread_mutex_unlock(&threadPool.mutex);
            }
            
            if(readyList[i].data.fd == exitPipe[0]){
                // for(int j=0; j<workerNum; j++){
                //     pthread_cancel(threadPool.tidArr[j]);
                // }
                puts("I am going to die!");
                threadPool.isThreadPoolRunning = 0;
                pthread_cond_broadcast(&threadPool.cond);
                for(int j=0; j<workerNum; ++j){
                    pthread_join(threadPool.tidArr[j], NULL);
                }
                puts("exit");
                exit(0);
            }
        }
    }
    closelog();
}